<script lang="tsx" setup>
import { useI18n } from 'vue-i18n'
import { onMounted, ref, onBeforeUnmount } from 'vue'
import { useCaptcha, useForm } from '@alova/scene-vue'
import { useNotification } from '@/hooks/useNotification'
import { CustomErrorCode, CustomSuccessCode } from '@/api/enums'
import { ApiSendPhoneVerifyCode, ApiRegister } from '@/api/user'
import { IconUser, IconLock, IconPhone } from '@arco-design/web-vue/es/icon'

import '@arco-design/web-vue/es/button/style/index.css'

import type { Schema } from '../../locale/schema'
import type { FieldRule } from '@arco-design/web-vue/es/form/interface'
import router from '@/router'

const { t } = useI18n<[Schema]>()
const { Notification } = useNotification()

/** @desc 验证码发送, 倒计时60s */
const {
  countdown,
  loading: sending,
  send: sendVerifyCode,
  onSuccess: onSendVerifyCodeSuccess
} = useCaptcha(() => ApiSendPhoneVerifyCode(form.value.phone))
onSendVerifyCodeSuccess(({ data }) => {
  localStorage.setItem('verifyDisableEndTime', new Date().getTime() / 1000 + 60 + '')
  return data.code === 20000
    ? Notification.success({ content: t('register.form.verifyCode.send.success') })
    : Notification.error({ content: t('register.form.verifyCode.send.error') })
})

/** @desc localStorage持久化验证码倒计时 */
let timer = 0
onMounted(() => {
  const verifyDisableEndTime = localStorage.getItem('verifyDisableEndTime')
  if (verifyDisableEndTime) {
    const endTime = +verifyDisableEndTime - new Date().getTime() / 1000
    endTime >= 1 && disableSendVerifyCode(~~endTime)
  }
})
onBeforeUnmount(() => {
  clearInterval(timer)
  countdown.value <= 0 && localStorage.removeItem('verifyDisableEndTime')
})
function disableSendVerifyCode(time: number) {
  countdown.value = time
  timer = time !== 0 ? setInterval(() => --countdown.value <= 0 && clearInterval(timer), 1000) : 0
}

/** @desc 注册表单 */
const { form, send, onSuccess, loading } = useForm(
  ({ password2: _, ...payload }) => ApiRegister(payload),
  {
    initialForm: {
      username: '',
      password: '',
      password2: '',
      phone: '',
      verifyCode: ''
    }
  }
)
const FORM = ref()
async function handleSendVerifyCode() {
  const { phone } = (await FORM.value.validateField('phone')) ?? {}
  return !phone ? sendVerifyCode() : Notification.error({ content: phone.message })
}
async function handleSignUp() {
  return (await FORM.value.validate())
    ? Notification.error({ content: t('register.form.error') })
    : send()
}
onSuccess(({ data }) => {
  switch (data.code) {
    case CustomSuccessCode.OK:
      Notification.success({ content: t('register.form.success') })
      localStorage.setItem('token', data.data.access_token)
      return router.push('/0')
    case CustomErrorCode.VERIFY_CODE_ERROR:
      return Notification.error({ content: t('register.api.verifyCode.error') })
    case CustomErrorCode.VERIFY_CODE_EXPIRED:
      return Notification.error({ content: t('register.api.verifyCode.expired') })
    case CustomErrorCode.VERIFY_CODE_FREQUENT:
      return Notification.error({ content: t('register.api.verifyCode.frequent') })
    case CustomErrorCode.BAD_REQUEST:
      return Notification.error({ content: t('register.api.username.exist') })
  }
})

/** @desc 切换登录/注册页 */
const toggleCard = defineEmits(['toggle'])
function handleLogin() {
  toggleCard('toggle')
}

/** @desc 表单字段信息（去除验证码字段） */
interface FormItemInfo {
  placeholder: string
  icon: JSX.Element
  component: JSX.Element
  rules: FieldRule | FieldRule[]
}
const formItems: Record<keyof Omit<typeof form.value, 'verifyCode'>, FormItemInfo> = {
  username: {
    placeholder: t('register.form.username'),
    icon: <IconUser />,
    component: <a-input></a-input>,
    rules: [
      { required: true, message: t('register.form.username.empty') },
      { minLength: 3, message: t('register.form.username.minLength', { minLength: 3 }) },
      { maxLength: 16, message: t('register.form.username.maxLength', { maxLength: 16 }) }
    ]
  },
  phone: {
    placeholder: t('register.form.phone'),
    icon: <IconPhone />,
    component: <a-input></a-input>,
    rules: [
      { required: true, message: t('register.form.phone.empty') },
      { match: /^1[3456789]\d{9}$/, message: t('register.form.phone.error') }
    ]
  },
  password: {
    placeholder: t('register.form.password'),
    icon: <IconLock />,
    component: <a-input-password></a-input-password>,
    rules: [
      { required: true, message: t('register.form.password.empty') },
      { minLength: 6, message: t('register.form.password.minLength', { minLength: 6 }) },
      { maxLength: 16, message: t('register.form.password.maxLength', { maxLength: 16 }) }
    ]
  },
  password2: {
    placeholder: t('register.form.password2'),
    icon: <IconLock />,
    component: <a-input-password></a-input-password>,
    rules: {
      validator: (value: string, addError) =>
        value !== form.value.password && addError(t('register.form.password2.error'))
    }
  }
}
</script>

<template>
  <div class="text-center !-pl-4" shadow="gray-300 dark:gray-600 2xl" bg="light-900 dark:gray-800">
    <h2 class="text-2xl font-bold font-serif">{{ $t('login.title') }}</h2>

    <div flex="~ col lg:row">
      <div
        class="after:(relative left-1/4 z-0 block h-1/2 w-1/2 rounded-full blur-2xl content-[''] -top-4/5)"
        after:bg="gradient-to-t"
        after:from="#fea92e"
        after:to="#70ce49"
        h="40 lg:90"
      >
        <img
          draggable="false"
          src="@/assets/images/plants/2.svg"
          class="relative z-1 mx-auto !h-40 lg:!h-90"
        />
      </div>
      <a-form ref="FORM" :model="form" class="mt-0 max-w-xs lg:mt-8">
        <a-form-item
          v-for="[field, { icon, placeholder, component, rules }] of Object.entries(formItems)"
          :key="field"
          :field="field"
          :rules="rules"
          hide-label
          class="!mb-4"
        >
          <component
            :is="component"
            v-model="form[field as keyof typeof form]"
            :autocomplete="false"
            :placeholder="placeholder"
          >
            <template #prefix>
              <component :is="icon" />
            </template>
          </component>
        </a-form-item>

        <a-form-item
          field="verifyCode"
          hide-label
          class="!mb-4"
          :rules="[
            { required: true, message: $t('register.form.verifyCode.empty') },
            { match: /^\d{6}$/, message: $t('register.form.verifyCode.error') }
          ]"
        >
          <a-input-search
            class="search"
            v-model="form.verifyCode"
            :placeholder="$t('register.form.verifyCode')"
            search-button
            :loading="sending || countdown > 0"
            @search="handleSendVerifyCode()"
          >
            <template #button-icon>
              <IconSend :rotate="-135" />
            </template>
            <template #button-default>
              <span>{{
                countdown > 0
                  ? $t('register.form.verifyCode.retry', { time: countdown })
                  : $t('register.form.verifyCode.send')
              }}</span>
            </template>
          </a-input-search>
        </a-form-item>

        <button
          type="button"
          class="mb-4 rounded-full px-8 py-2 text-[1.1rem] font-bold transition"
          bg="orange-300 hover:orange-200 active:orange-100"
          dark:bg="blue-gray-700 hover:blue-gray-900 active:blue-gray-800"
          @click="handleSignUp"
          :disabled="loading"
        >
          {{ $t('register.submit') }}
        </button>
        <button
          type="button"
          class="rounded-full px-8 py-2 text-[1.1rem] font-bold transition"
          bg="gray-200 hover:gray-300 active:gray-100"
          dark:bg="blue-gray-700 hover:blue-gray-900 active:blue-gray-800"
          @click="handleLogin"
        >
          {{ $t('register.login') }}
        </button>
      </a-form>
    </div>
  </div>
</template>

<style lang="css" scoped>
.search {
  --primary-7: 255, 237, 213;
  --primary-6: 253, 186, 116;
  --primary-5: 254, 215, 170;
}
.dark .search {
  --primary-7: 30, 41, 59;
  --primary-6: 51, 65, 85;
  --primary-5: 15, 23, 42;
}

:deep(.search .arco-btn) {
  @apply font-bold text-dark-500 hover:text-dark-500 dark:text-light-500;
}

:deep(.search .arco-btn.arco-btn-loading) {
  @apply bg-green-200 text-gray-600 dark:bg-blue-gray-900;
  @apply cursor-not-allowed;
}
</style>
